//

#ifndef Rectangle2D_h__
#define Rectangle2D_h__

#include "Point2D.h"
#include "Vector2D.h"


namespace Geometric
{
	class _GEOMETRIC Triangle2D
	{
	public:
		Triangle2D(const Point2D& _pt_a, const Point2D& _pt_b, const Point2D& _pt_c)
			:m_a(_pt_a), m_b(_pt_b), m_c(_pt_c)
		{
		}

		Triangle2D(const Triangle2D& _tri)
			:m_a(_tri.m_a), m_b(_tri.m_b), m_c(_tri.m_c)
		{
		}

		Triangle2D() :m_a(), m_b(), m_c()
		{
		}

		~Triangle2D()
		{
		}

	public:
		// three vertexes(a,b,c) of a triangle
		Point2D m_a;
		Point2D m_b;
		Point2D m_c;

	public:
		inline Triangle2D& operator = (const Triangle2D& _tri)
		{
			m_a = _tri.m_a;
			m_b = _tri.m_b;
			m_c = _tri.m_c;
			return (*this);
		}

	public:
		inline void set(const Point2D& _pt1, const Point2D& _pt2, const Point2D& _pt3)
		{
			m_a = _pt1;
			m_b = _pt2;
			m_c = _pt3;
		}

		inline bool isValid()
		{
			return (m_a != m_b && m_a != m_c && m_b != m_c);
		}

		inline bool isOverlap()
		{
			return false;
		}

		inline double getArea()
		{
			Vector2D ab, ac;
			ab.set(m_a, m_b);
			ac.set(m_a, m_c);
			return (0.5 * Gmath::toAbs(ab.getCross(ac)));
		}

		inline double getPerimeter()
		{
			Vector2D ab, ac, bc;
			ab.set(m_a, m_b);
			ac.set(m_a, m_c);
			bc.set(m_b, m_c);
			return (ab.getLength() + ac.getLength() + bc.getLength());
		}

		inline int getLocation(const Point2D& _point)
		{
			Vector2D vec1, vec2, vec3;
			double v1, v2, v3;

			vec1.set(m_a, m_b);
			vec2.set(m_a, m_c);
			vec3.set(m_a, _point);
			v1 = vec3.getCross(vec1) * vec3.getCross(vec2);
			if(Gmath::isMore(v1, 0) == true)
			{
				// v1>0: the point is outside the triangle
				return POINT_LCT_OUTSIDE;
			}

			vec1.set(m_b, m_a);
			vec2.set(m_b, m_c);
			vec3.set(m_b, _point);
			v2 = vec3.getCross(vec1) * vec3.getCross(vec2);
			if(Gmath::isMore(v2, 0) == true)
			{
				// v2>0: the point is outside the triangle
				return POINT_LCT_OUTSIDE;
			}

			vec1.set(m_c, m_b);
			vec2.set(m_c, m_a);
			vec3.set(m_c, _point);
			v3 = vec3.getCross(vec1) * vec3.getCross(vec2);
			if(Gmath::isMore(v3, 0) == true)
			{
				// v3>0: the point is outside the triangle
				return POINT_LCT_OUTSIDE;
			}


			// v1=0 or v2=0 or v3=0: the point is on the triangle
			if(Gmath::isZero(v1) == true && Gmath::isZero(v2) == true 
				&& Gmath::isZero(v3) == true)
			{
				return POINT_LCT_ON;
			}

			// v1<0 and v2<0 and v3<0: the point is inside the triangle
			return POINT_LCT_INSIDE;
		}

	};



	class _GEOMETRIC Rectangle2D
	{
	public:
		Rectangle2D(const Point2D& _a, const Point2D& _b, const Point2D& _c)
			:m_a(_a), m_b(_b), m_c(_c)
		{
		}

		Rectangle2D(const Rectangle2D& _rect)
			:m_a(_rect.m_a), m_b(_rect.m_b), m_c(_rect.m_c)
		{
		}

		Rectangle2D() :m_a(), m_b(), m_c()
		{
		}

		~Rectangle2D()
		{
		}


	public:
		// three vertexes(a,b,c) represent a rectangle
		// point c is the pedal in a right triangle
		Point2D m_a;
		Point2D m_b;
		Point2D m_c;

	public:
		Rectangle2D& operator = (const Rectangle2D& _rect)
		{			
			m_a = _rect.m_a;
			m_b = _rect.m_b;
			m_c = _rect.m_c;
			return (*this);
		}

	public:
		inline void set(const Point2D& _a, const Point2D& _b, const Point2D& _c)
		{
			m_a = _a;
			m_b = _b;
			m_c = _c;
		}

		inline bool isValid()
		{
			Vector2D ca, cb;
			ca.set(m_a.m_x - m_c.m_x, m_a.m_y - m_c.m_y);
			cb.set(m_b.m_x - m_c.m_x, m_b.m_y - m_c.m_y);
			return ca.isVertical(cb);
		}

		inline bool isOverlap(const Rectangle2D& _rect)
		{
			if(m_c == _rect.m_c)
			{
				if((m_b == _rect.m_b && m_a == _rect.m_a)
					|| (m_b == _rect.m_a && m_a == _rect.m_b))
				{
					return true;
				}
			}
			return false;
		}

		inline const Point2D& getA()
		{
			return m_a;
		}

		inline const Point2D& getB()
		{
			return m_b;
		}

		inline const Point2D& getC()
		{
			return m_c;
		}

		// get the other point(not the A,B,C)
		inline const Point2D& getD()
		{
			static Point2D d;
			d.set(m_a.m_x + m_b.m_x - m_c.m_x, m_a.m_y + m_b.m_y - m_c.m_y);
			return d;
		}

		inline const Point2D& getVectex(unsigned int _index)
		{
			if(_index < 2)
			{
				return (_index == 1) ? m_a : m_b;
			}
			else
			{
				return (_index == 2) ? m_c : getD();
			}
		}	

		// get area of the rectangle
		inline double getArea()
		{
			Vector2D ab, ac;
			ab.set(m_b.m_x - m_a.m_x, m_b.m_y - m_a.m_y);
			ac.set(m_c.m_x - m_a.m_x, m_c.m_y - m_a.m_y);
			return Gmath::toAbs(ab.getCross(ac));
		}

		// get perimeter of the rectangle
		inline double getPerimeter()
		{
			Vector2D ab, ac, bc;
			ab.set(m_b.m_x - m_a.m_x, m_b.m_y - m_a.m_y);
			ac.set(m_c.m_x - m_a.m_x, m_c.m_y - m_a.m_y);
			return (Gmath::toSqrt(ab.getSquare()) + Gmath::toSqrt(ac.getSquare()));
		}

		inline int getLocation(const Point2D& _point)
		{
			Vector2D vec1, vec2, vec3, vec4;
			double v1, v2;
			Point2D d;

			// get the other point in a rectangle
			d = getD();

			vec1.set(m_c, _point);
			vec2.set(m_c, m_a);
			vec3.set(m_b, _point);
			vec4.set(m_b, d);
			v1 = vec1.getCross(vec2) * vec3.getCross(vec4);
			if(Gmath::isMore(v1, 0) == true)
			{
				// v1>0: the point is outside the rectangle
				return POINT_LCT_OUTSIDE;
			}

			vec1.set(m_c, _point);
			vec2.set(m_c, m_b);
			vec3.set(m_a, _point);
			vec4.set(m_a, d);
			v2 = vec1.getCross(vec2) * vec3.getCross(vec4);
			if(Gmath::isMore(v2, 0) == true)
			{
				// v2>0: the point is outside the rectangle
				return POINT_LCT_OUTSIDE;
			}


			// v1=0 or v2=0: the point is on the rectangle
			if(Gmath::isEqual(v1 * v2, 0) == true)
			{
				return POINT_LCT_ON;
			}

			// v1<0 and v2<0: the point is inside the triangle
			return POINT_LCT_INSIDE;
		}

		

	};

}

#endif
